home *** CD-ROM | disk | FTP | other *** search
- *
- * mfc-parallel (ProNET driver)
- *
-
- ******* pronet-driver/--Overview-- ******************************************
- *
- * This file covers information on how to write custom ProNET drivers.
- * As is usual with my distributions ;-(, the API has changed again, but if
- * there was really a single custom driver out, it can be changed really easy.
- *
- * A ProNET driver is an ordinary executable residing in the DEVS:ProNET/
- * directory. This executable will be loaded for every ProNET Unit requiring
- * it; it doesn't have to be reentrant. The code is entered at the very
- * first position, which must contain the Init routine.
- *
- * >> CHANGES FROM V2 DRIVERS <<
- *
- * New in V3: Exit-Routine, Init-Flags = 1
- * New in v37: ReadFlush-Routine, Init-Flags (now called Version) = 2,
- * Write only one chunk, Write timeout support (three return codes now).
- *
- *****************************************************************************
- *
- *
-
- ;**** The MFCDevKit is not very extensive in explaining how the PIA
- ;**** chips work, so I had to search for the stuff myself. Motorola's
- ;**** Internet site doesn't know any of their old chips.
- ;**** The following text was found in the Internet in an article about
- ;**** the Dragon/Tandy hardware. I found `m6821.html' via Altavista and
- ;**** the keywords 'mc 6821 pia' or similar.
- ;
- ; The Dragon and Tandy microcomputers have two Motorola 6821 peripheral
- ; interface adapters (PIA) mapped into the address space between $FF00 and
- ; $FF3F. The first of these, PIA-0, is located $FF00-$FF03, and the second,
- ; PIA-1, is located $FF20-$FF23. The Dragon 64 also has a third PIA at
- ; $FF04-$FF07 but this is not discussed here.
- ;
- ; Each PIA contains two 8-bit I/O ports, port A and port B, where each port
- ; and each of the 16 I/O pins are independently programmable as either input
- ; or output pins. Two control/interrupt lines are also associated with each
- ; port, allowing one extra input/output pin and one input-only pin per port.
- ; These pins can trigger an interrupt upon 0->1 or 1->0 transitions, or
- ; 'follow' outputs, and are therefore slightly more sophisticated than simple
- ; buffers, but can nevertheless be used simply as additional I/O.
- ;
- ; Each port is accessed and controlled through 3 registers. These are the
- ; peripheral data register (PDR), the data direction register (DDR) and the
- ; control register (CR). For PIA-0 port A the control register is mapped into
- ; the CPU address space at $FF01. However, only two memory addresses are used
- ; for each port and therefore both the PDR and DDR cannot be made available
- ; simultaneously. Instead bit 2 of the control register must be set or cleared
- ; to select whether it is the PDR or DDR which is visible at $FF00. This
- ; applies similarly to port B on PIA-0, and both ports of PIA-1.
- ;
- ; In normal operation the PDR is left visible for each port, with the DDR
- ; only made visible when necessary during configuration or reconfiguration.
- ;
- ; The format of the port registers is as follows:
- ;
- ; PDR - 8 bits of data as currently being input/output on the device pins.
- ; DDR - 8 bits, each indicating whether the corresponding PDR bits are for
- ; input (0) or output (1).
- ; CR - bit 7: control line 1 interrupt status (0 = idle, 1 = request) RO
- ; bit 6: control line 2 interrupt status (0 = idle, 1 = request) RO
- ; bit 5: control line 2 direction (0 = input, 1 = output) RW
- ; bit 4: control line 2 mode (0 = 1->0, 1 = 0->1 or 'follow') RW
- ; bit 3: control line 2 enable (0 = disable, 1 = enable) RW
- ; bit 2: PDR/DDR select (0 = DDR, 1 = PDR) RW
- ; bit 1: control line 1 mode (0 = 1->0, 1 = 0->1) RW
- ; bit 0: control line 1 enable (0 = disable, 1 = enable) RW
- ;
- ; RO = Read only
- ; RW = Read/write
- *
- * On the Amiga, all PIA registers are ZERO by default (after power-on).
- *
-
- ; Format of one packet is:
- ; 2bytes LENGTH of data including next two words in WORDS-1 !!!!!
- ; 2bytes Destination port
- ; 2bytes Source port
- ; xbytes DATA
-
- * I know this is HW banging, but at the moment I don't know how to do
- * it in a different way.
-
- DISABLEINTS macro
- move.w #$4000,$dff09a
- endm
- ENABLEINTS macro
- move.w #$c000,$dff09a
- endm
-
- include "A:OSmacros.i"
- include "exec/exec.i"
- include "hardware/mfcres.i"
- include "hardware/piabits.i"
- include "hardware/intbits.i"
- include "P:include/devices/pronet.i"
- include "exec_lib.i"
- include "intuition_lib.i"
- include "dos_lib.i"
-
- ******* pronet-driver/Init **************************************************
- *
- * NAME
- * Init -- Initialize a ProNET driver.
- *
- * SYNOPSIS
- * error = Init(drvdata, confstr, ID, version);
- * D0 A0 A1 D0 D1
- *
- * STRPTR Init(struct PNDrvData*, STRPTR, ULONG, ULONG);
- *
- * FUNCTION
- * This function is called whenever pronet.device launches a new Unit
- * process.
- *
- * INPUTS
- * drvdata - pointer to struct PNDrvData which must be filled out by
- * this routine.
- * confstr - pointer to the configuration string following the driver
- * ID.
- * ID - D0 contains the bytes "RST!" (not a pointer!). Check this to
- * prevent unwanted effects when your driver is started by
- * accident.
- * version - This number identifies the version of pronet.device that is
- * calling this driver. 0=V2, 1=V3, 2=v37; 0 and 1
- * should be rejected with error code NULL!.
- *
- * RESULT
- * error - NULL if everything went ok, or a pointer to an error string,
- * of which ProNET will make a copy and hand it to the calling
- * application. You could also return one of the magic cookies
- * as defined in devices/pronet.h for standard errors.
- *
- * SEE ALSO
- * devices/pronet.h
- *
- *****************************************************************************
- *
- *
-
- Init movem.l d2-d7/a2-a6,-(sp)
- move.l a0,a4
- move.l a1,a5
- cmp.l #"RST!",d0 ;compare ID
- bne .ende
- cmp.l #2,d1 ;0 was the error condition
- bne .ende ;in ProNET V2/V3
-
- move.l #PNDRVERR_WRONG_ARGS,RC
- move.l a5,a0
- bsr dec2slong
- tst.w d4
- bmi.s .ende
- move.w d1,PORT
- bsr dec2slong
- tst.w d4
- bmi.s .ende
- move.w d1,MACHINE
- bsr dec2slong
- tst.w d4
- bmi.s .ende
- move.w d1,PRIORITY
-
- bsr AllocPPort
- tst.w d0
- bne.s .ende
-
- bsr InitTransfer
-
- move.l 4.w,a6
- move.l ThisTask(a6),a1
- move.w PRIORITY(pc),d0
- LIBCALL SetTaskPri
-
- move.b INTsigbit(pc),pndd_ReadSignalBit(a4)
- move.l #ReadQuery,pndd_ReadQuery(a4)
- move.l #ReadFlush,pndd_ReadFlush(a4)
- move.l #Read,pndd_Read(a4)
- move.l #Write,pndd_Write(a4)
- move.l #Exit,pndd_Exit(a4)
-
- clr.l RC
-
- .ende movem.l (sp)+,d2-d7/a2-a6
- move.l RC(pc),d0
- rts
-
- RC dc.l 0
-
- dc.b "$VER: mfc-parallel 37.0 (30.11.96)",13,10,0
- even
-
- PORT dc.w 0
- MACHINE dc.w 0
- PRIORITY dc.w 0
-
- dec2slong ; konvertiert Dezimalstring ab (a0) zu Longword in D1 !!
- ; SIGNED!
- moveq #-1,d4
- moveq #0,d1
- moveq #0,d3
- move.b (a0),d0
- cmp.b #"-",d0
- bne.s .loop
- addq.l #1,a0
- st d3
- .loop moveq #0,d0
- move.b (a0)+,d0
- sub.b #"0",d0
- cmp.b #9,d0
- bhi.s .oki
- clr.l d4
- move.l d1,d2
- lsl.l #3,d1
- add.l d2,d1
- add.l d2,d1
- add.l d0,d1
- bra.s .loop
- .oki tst.w d3
- beq.s .ende
- neg.l d1
- .ende rts
-
- ******* pronet-driver/Exit **************************************************
- *
- * NAME
- * Exit -- Terminate a ProNET driver.
- *
- * SYNOPSIS
- * Exit();
- *
- * void Exit(void);
- *
- * FUNCTION
- * This function is called whenever pronet.device launches a new Unit
- * process. You must free all resources you allocated previously and
- * return after that.
- *
- *****************************************************************************
- *
- *
-
- Exit movem.l d2-d7/a2-a6,-(sp)
- bsr ExitTransfer
- bsr FreePPort
- movem.l (sp)+,d2-d7/a2-a6
- rts
-
- ******* pronet-driver/ReadQuery *********************************************
- *
- * NAME
- * ReadQuery -- find out about an incoming packet.
- *
- * SYNOPSIS
- * length, destport, srcport = ReadQuery();
- * D0 D1 D2
- *
- * UWORD, UWORD, UWORD = ReadQuery(void);
- *
- * FUNCTION
- * This function is called whenever the Unit's task was woken up by
- * the signal bit you provided in drvdata->ReadSignalBit in the Init
- * routine. The signal bit can either be the signal bit of an
- * IORequest reply port or one triggered from an interrupt or
- * process set up by you.
- *
- * This function call is used to find out some basic info
- * about the incoming packet before reading the actual data.
- *
- * RESULT
- * length - number of bytes of the actual packet data (see NOTES)
- * destport - destination ProNET Port on this machine
- * srcport - source Port on the remote machine
- *
- * NOTES
- * If you find out that the ReadSignalBit was triggered by mistake,
- * you can return 0 in the length word, in which case ProNET will go
- * to sleep again. Otherwise it is *guaranteed* that either ReadFlush
- * or Read will be called lateron, no other driver routines will be
- * called in between.
- *
- *****************************************************************************
- *
- *
-
- ReadQuery movem.l d3-d7/a2-a6,-(sp)
-
- move.l PIAaddr(pc),a3
- btst #PIAPAB_BUSY,PIA_PRA(a3)
- bne.s .error ; check if packet is pending
-
- bclr #PIACRB_DDR,PIA_CRA(a3)
- bset #PIAPAB_PAPEROUT,PIA_PRA(a3) ;set POUT(ack) line to output
- bset #PIACRB_DDR,PIA_CRA(a3)
-
- lea buffer-2(pc),a4 ; first word is `empty'
- moveq #3,d4 ; (due to handshake init)
- bsr ReceiveData
- movem.w buffer(pc),d0-d2
- subq.w #2-1,d0
- add.w d0,d0
- movem.l (sp)+,d3-d7/a2-a6
- rts
- .error moveq #0,d0
- movem.l (sp)+,d3-d7/a2-a6
- rts
-
- ******* pronet-driver/ReadFlush *********************************************
- *
- * NAME
- * ReadFlush -- Forget the incoming packet.
- *
- * SYNOPSIS
- * ReadFlush();
- *
- * void ReadFlush(void);
- *
- * FUNCTION
- * This function will only be called if a previous ReadQuery succeeded.
- * ProNET wants us to forget the complete packet.
- *
- *****************************************************************************
- *
- *
-
- ReadFlush movem.l d2-d7/a2-a6,-(sp)
- move.w buffer(pc),d4
- subq.w #2,d4
- bsr FlushData
- bra.s read_entry
-
- ******* pronet-driver/Read **************************************************
- *
- * NAME
- * Read -- read the incoming packet data to a specified memory location.
- *
- * SYNOPSIS
- * Read(memptr);
- * A0
- *
- * void Read(APTR);
- *
- * FUNCTION
- * This function will only be called if a previous ReadQuery succeeded.
- * Here we will copy the packet's data to the specified memory location.
- *
- * INPUTS
- * memptr - memory location to which we shall copy
- *
- *****************************************************************************
- *
- *
-
- Read movem.l d2-d7/a2-a6,-(sp)
- move.l a0,a4
- move.w buffer(pc),d4
- subq.w #2,d4
- bsr ReceiveData
-
- read_entry move.l PIAaddr(pc),a3
- bclr #PIACRB_DDR,PIA_CRA(a3)
- bclr #PIAPAB_PAPEROUT,PIA_PRA(a3) ;set POUT(ack) line to input
- bset #PIACRB_DDR,PIA_CRA(a3)
-
- movem.l (sp)+,d2-d7/a2-a6
- rts
-
-
- ******* pronet-driver/Write *************************************************
- *
- * NAME
- * Write -- Send a data packet to the remote machine.
- *
- * SYNOPSIS
- * error = Write(dataptr, length, destport, srcport);
- * D0 A0 D0 D1 D2
- *
- * ULONG Write(APTR, ULONG, UWORD, UWORD);
- *
- * FUNCTION
- * I guess this will always be the most complex function of a driver.
- * Try to get a connection to the remote machine and send the packet
- * as requested. A kind of flow control, like a timeout feature,
- * should be implemented.
- *
- * INPUTS
- * dataptr - pointer to the data to be transmitted
- * length - length of data to be transmitted. The length is limited
- * to 16384 bytes and it is even.
- * destport - destination Port on the remote machine
- * srcport - source Port on this machine
- *
- * RESULT
- * error - 0 if packet could be sent without trouble.
- * -1 if the line is currently busy -- pronet.device will then
- * try again later.
- * 1 if the remote machine does not respond. The application
- * will be notified in this case, CMD_WRITE returns with
- * an error (PNDERR_DESTINATION_GONE).
- *
- * NOTES
- * Your driver is responsible for correct transmission of the data,
- * by using checksums or similar mechanisms.
- *
- *****************************************************************************
- *
- *
-
- Write movem.l d2-d7/a2-a6,-(sp)
-
- lea buffer(pc),a5
- move.l a0,6(a5)
- move.l d0,10(a5)
-
- move.w d1,(a5)
- move.w d2,2(a5)
-
- move.w d0,d4
- lsr.w #1,d4
- addq.w #2-1,d4
- bsr AcquireLine
- tst.w d0 ;Line is busy now! Try later
- bne.s .ende
-
- move.l a5,a4
- moveq #1,d4
- moveq #0,d6
- bsr SendData
-
- move.l 6(a5),a4 ; The last word gets sent separately.
- move.l 10(a5),d4
- lsr.w #1,d4
- subq.w #2,d4
- bmi.s .0
- bsr SendData
-
- .0 DISABLEINTS * We don't take too long here
- moveq #0,d4 * in case the other machine wants
- bsr SendData * to send something...
-
- clr.w sending
- bsr InitParallel
- ENABLEINTS
-
- moveq #0,d0
- .ende movem.l (sp)+,d2-d7/a2-a6
- rts
-
- dc.w 0
- buffer dc.w 0,0,0
- dc.l 0,0
-
-
- ; -- Allocate the specified MFC port.
- AllocPPort move.l 4.w,a6
- lea mfcname(pc),a1
- LIBCALL OpenResource
- move.l #err1,RC
- move.l d0,mfcbase
- beq.s APP_nores
-
- move.l d0,a6
- moveq #0,d0
- move.w PORT(pc),d0
- lea drivername(pc),a0
- LIBCALL AllocPort
- move.l #err2,RC
- move.l d0,portnode
- beq.s APP_noport
-
- move.l d0,a0
- move.l port_ChipNode(a0),a0
- move.l chip_Product(a0),d0
- move.l #err3,RC
- cmp.l #CHIP_PIA,d0
- bne.s APP_wrongcard
- move.l chip_Address(a0),PIAaddr
-
- moveq #0,d0
- rts
-
- FreePPort
- APP_wrongcard move.l mfcbase(pc),a6
- move.l portnode(pc),a0
- LIBCALL FreePort
- APP_noport
- APP_nores moveq #-1,d0
- rts
-
- mfcname dc.b "mfc.resource",0
- drivername dc.b "ProNET MultiFaceCard3 Driver",0
- err1 dc.b "Can't find mfc.resource.",0
- err2 dc.b "Can't open specified MFC parallel port.",0
- err3 dc.b "The specified port is no MC6821 PIA.",0
- even
- mfcbase dc.l 0
- portnode dc.l 0
- PIAaddr dc.l 0
-
- ; -- Init Transfer routines
- InitTransfer move.l 4.w,a6
- move.l ThisTask(a6),INTsigtask
-
- moveq #-1,d0
- LIBCALL AllocSignal
- move.b d0,INTsigbit
-
- move.l 4.w,a6
- moveq #INTB_PORTS,d0
- lea INTstruct(pc),a1
- LIBCALL AddIntServer
-
- bsr InitParallel
- rts
-
- ExitTransfer
- bsr ExitParallel
-
- move.l 4.w,a6
- moveq #INTB_PORTS,d0
- lea INTstruct(pc),a1
- LIBCALL RemIntServer
-
- move.b INTsigbit(pc),d0
- LIBCALL FreeSignal
- rts
-
- INTsigtask dc.l 0
- INTsigbit dc.w 0
-
- INTstruct dc.l 0,0
- dc.b 2,0
- dc.l drivername
- dc.l 0,INTcode
- INTcode moveq #0,d0
- move.l PIAaddr(pc),a0
- btst #PIACRB_IRQ1,PIA_CRB(a0) ;PIA IRQ1?
- beq.s .no
- move.b PIA_PRB(a0),d1 ;Clear IRQ bit
- tst.w sending
- bne.s .no
- btst #PIAPAB_BUSY,PIA_PRA(a0) ;check if we
- bne.s .no ;were really called
- move.l 4.w,a6 ;or other machine
- move.l INTsigtask(pc),a1 ;was switched on!
- move.b INTsigbit(pc),d1
- moveq #0,d0
- bset d1,d0
- LIBCALL Signal
- moveq #1,d0
- .no rts
-
- ;:---------------------------------------------------------------------------
- ;:-- InitParallel
- InitParallel
- move.l PIAaddr(pc),a0
-
- move.b #%00000100,PIA_CRA(a0)
- move.b #%00000111,PIA_PRA(a0) ;control lines to 1 (when we set them to output)
- move.b #%00000000,PIA_CRA(a0)
- move.b #%00000000,PIA_PRA(a0) ;control lines -> input
- move.b #%00000100,PIA_CRA(a0) ;PR-Register is default
-
- move.b #%00000000,PIA_CRB(a0)
- sf PIA_PRB(a0) ;data lines input
- move.b #%00000101,PIA_CRB(a0) ;CB1-interrupt enablen
- rts
-
- ExitParallel
- move.l PIAaddr(pc),a0
- move.b #%00000100,PIA_CRB(a0) ;C1-interrupt disablen
- rts
-
-
- ;:---------------------------------------------------------------------------
- ;:-- AcquireLine
- ;:-- d4 Number of words-1 that will be sent !
- ;:-- !!!!!! RETURNS d0=0 if no error
- ;:-- d0=-1 if line was busy
- AcquireLine
- move.l PIAaddr(pc),a3
-
- DISABLEINTS
- btst #PIAPAB_BUSY,PIA_PRA(a3)
- bne.s .free
- ENABLEINTS
- moveq #-1,d0
- rts
-
- .free st sending
-
- * Set BUSY line
- bclr #PIACRB_DDR,PIA_CRA(a3)
- bset #PIAPAB_BUSY,PIA_PRA(a3) ;BUSY = output
- bset #PIACRB_DDR,PIA_CRA(a3)
- bclr #PIAPAB_BUSY,PIA_PRA(a3) ;BUSY = 0
-
- * Toggle SELECT line to cause interrupt on server
- bclr #PIACRB_DDR,PIA_CRA(a3)
- bset #PIAPAB_SELECTED,PIA_PRA(a3) ;output
- bset #PIACRB_DDR,PIA_CRA(a3)
- bclr #PIAPAB_SELECTED,PIA_PRA(a3) ;toggle
- bset #PIAPAB_SELECTED,PIA_PRA(a3) ;
- bclr #PIACRB_DDR,PIA_CRA(a3)
- bclr #PIAPAB_SELECTED,PIA_PRA(a3) ;input
- bset #PIACRB_DDR,PIA_CRA(a3)
-
- ENABLEINTS
-
- tst.w firstpacketflag ; no timeout for the first packet!
- beq.s .acknotimeout
- tst.w MACHINE ; no timeout for machine 1!
- bne.s .acknotimeout
-
- move.l #100000,d0
- .ack0 btst #PIAPAB_PAPEROUT,PIA_PRA(a3) ; wait for it to respond...
- beq.s .ack0ok
- subq.l #1,d0
- bne.s .ack0
-
- bset #PIAPAB_BUSY,PIA_PRA(a3)
- bsr InitParallel ;Timeout! Reset all lines!
- bsr INTcode
- moveq #-1,d0
- rts
-
- .acknotimeout btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
- bne.s .acknotimeout
-
- .ack0ok st firstpacketflag
- bset #PIAPAB_BUSY,PIA_PRA(a3) ; and initialize handshake sequence
- .ack1 btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
- beq.s .ack1
-
- bclr #PIACRB_DDR,PIA_CRB(a3)
- st PIA_PRB(a3) ; DATA = output
- bset #PIACRB_DDR,PIA_CRB(a3)
-
- ror.w #8,d4 ; and hand the length word over
- move.b d4,PIA_PRB(a3) ; to the remote machine
- bclr #PIAPAB_BUSY,PIA_PRA(a3)
- ror.w #8,d4
- .ack2 btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
- bne.s .ack2
- move.b d4,PIA_PRB(a3)
- bset #PIAPAB_BUSY,PIA_PRA(a3)
- .ack3 btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
- beq.s .ack3
- moveq #0,d0
- rts
-
- firstpacketflag dc.w 0
- sending dc.w 0
-
- ;:---------------------------------------------------------------------------
- ;:-- SendData
- ;:-- a4 *data
- ;:-- d4 datalength in words-1
- SendData
- move.l PIAaddr(pc),a2
- lea PIA_PRB(a2),a0
- lea PIA_PRA(a2),a2
-
- move.b #%11111110,d0 ; clear BUSY
- move.b #%00000001,d1 ; set BUSY
-
- .loop move.b (a4)+,(a0) ; fully handshaked data txfer
- and.b d0,(a2)
- .ack0 btst #PIAPAB_PAPEROUT,(a2)
- bne.s .ack0
- move.b (a4)+,(a0)
- or.b d1,(a2)
- .ack1 btst #PIAPAB_PAPEROUT,(a2)
- beq.s .ack1
- dbra d4,.loop
- rts
-
- ;:---------------------------------------------------------------------------
- ;:-- ReceiveData
- ;:-- a4 *datadest
- ;:-- d4 datalength in words-1
- ReceiveData
- move.l PIAaddr(pc),a2
- lea PIA_PRB(a2),a0
- lea PIA_PRA(a2),a2
-
- move.b #%11111101,d0 ; clear POUT
- move.b #%00000010,d1 ; set POUT
-
- .loop btst #PIAPAB_BUSY,(a2)
- bne.s .loop
- move.b (a0),(a4)+
- and.b d0,(a2)
- .ack0 btst #PIAPAB_BUSY,(a2)
- beq.s .ack0
- move.b (a0),(a4)+
- or.b d1,(a2)
- dbra d4,.loop
- rts
-
- FlushData
- move.l PIAaddr(pc),a2
- lea PIA_PRB(a2),a0
- lea PIA_PRA(a2),a2
-
- move.b #%11111101,d0 ; clear POUT
- move.b #%00000010,d1 ; set POUT
-
- .loop btst #PIAPAB_BUSY,(a2)
- bne.s .loop
- and.b d0,(a2)
- .ack0 btst #PIAPAB_BUSY,(a2)
- beq.s .ack0
- or.b d1,(a2)
- dbra d4,.loop
- rts
-